package com.bw.dao.impl;


import com.bw.util.JDBCUtil;

import java.lang.reflect.Field;
import java.sql.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * BaseDao 是 增删改查工具类
 */


//登录，回显 baseQueryOne
//列表，动态下拉框  baseQueryList
//统计总条数 baseQueryCount
//修改，添加，删除  baseUpdate
//多对多 添加时获取主键id  baseInsertWithId

public class BaseDao {


    // 公共的查询方法  返回的是对象的集合 Object... args 可变参数
    public <T> List<T> baseQueryList(Class<T> clazz, String sql, Object... args) {
        List<T> list = new ArrayList<>();
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        int rows = 0;
        try {
            connection = JDBCUtil.getConn();
            // 准备语句对象
            preparedStatement = connection.prepareStatement(sql);
            // 设置语句上的参数
            for (int i = 0; i < args.length; i++) {
                preparedStatement.setObject(i + 1, args[i]);
            }
            // 执行 查询
            resultSet = preparedStatement.executeQuery();
            ResultSetMetaData metaData = resultSet.getMetaData();
            int columnCount = metaData.getColumnCount();

            // 将结果集通过反射封装成实体类对象
            while (resultSet.next()) {
                // 使用反射实例化对象
                Object obj = clazz.getDeclaredConstructor().newInstance();

                for (int i = 1; i <= columnCount; i++) {
                    //获取数据库的字段名
                    String columnName = metaData.getColumnLabel(i);
                    //根据字段名 获取值
                    Object value = resultSet.getObject(columnName);

                    //获取属性（成员变量）成员变量类型必须是引用数据类型
                    Field field = clazz.getDeclaredField(underScoreToCamelCase(columnName));
                    field.setAccessible(true);//暴力破解

                    if (null!=value){
                        // 处理datetime类型字段和java.util.Data转换问题
                        if (value.getClass().equals(LocalDateTime.class)) {
                            value = Timestamp.valueOf((LocalDateTime) value);
                        }
                        System.out.println(field.getGenericType() +" === > "+value);
                        //给属性赋值
                        field.set(obj, value);
                    }else {
                        field.set(obj, null);
                    }
                }
                list.add((T) obj);
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (null != resultSet) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
            if (null != preparedStatement) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }

        }
        return list;
    }

	
	 // 公共的查询方法  返回的是对象的集合 Object... args 可变参数
    public <T>  T  baseQueryOne(Class<T> clazz, String sql, Object... args) {
        List<T> list = new ArrayList<>();
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        int rows = 0;
        try {
            connection = JDBCUtil.getConn();
            // 准备语句对象
            preparedStatement = connection.prepareStatement(sql);
            // 设置语句上的参数
            for (int i = 0; i < args.length; i++) {
                preparedStatement.setObject(i + 1, args[i]);
            }
            // 执行 查询
            resultSet = preparedStatement.executeQuery();
            ResultSetMetaData metaData = resultSet.getMetaData();
            int columnCount = metaData.getColumnCount();

            // 将结果集通过反射封装成实体类对象
            while (resultSet.next()) {
                // 使用反射实例化对象
                Object obj = clazz.getDeclaredConstructor().newInstance();

                for (int i = 1; i <= columnCount; i++) {
                    //获取数据库的字段名
                    String columnName = metaData.getColumnLabel(i);
                    //根据字段名 获取值
                    Object value = resultSet.getObject(columnName);

                    //获取属性（成员变量）成员变量类型必须是引用数据类型
                    Field field = clazz.getDeclaredField(underScoreToCamelCase(columnName));
                    field.setAccessible(true);//暴力破解

                    if (null!=value){
                        // 处理datetime类型字段和java.util.Data转换问题
                        if (value.getClass().equals(LocalDateTime.class)) {
                            value = Timestamp.valueOf((LocalDateTime) value);
                        }
                        System.out.println(field.getGenericType() +" === > "+value);
                        //给属性赋值
                        field.set(obj, value);
                    }else {
                        field.set(obj, null);
                    }
                }
                list.add((T) obj);
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (null != resultSet) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
            if (null != preparedStatement) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }

        }
        return list.size()==0?null:list.get(0);
    }
	
	
    // 通用的增删改方法
    public int baseUpdate(String sql, Object... args) {

        // 获取连接
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        int rows = 0;
        try {
            connection = JDBCUtil.getConn();
            // 准备语句对象
            preparedStatement = connection.prepareStatement(sql);
            // 设置语句上的参数， 循环赋值
            for (int i = 0; i < args.length; i++) {
                if (null!=args[i]){
                    preparedStatement.setObject(i + 1, args[i]);
                }else {
                    preparedStatement.setObject(i + 1, 0);
                }
            }
            // 执行 增删改 executeUpdate
            rows = preparedStatement.executeUpdate();
            // 释放资源(可选)
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (null != preparedStatement) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        // 返回的是影响数据库记录数
        return rows;
    }

    //-------------------------------------------------------------
    // 通用的增删改方法
    public int baseUpdate(Connection connection, String sql, Object... args) {
        // 获取连接
        PreparedStatement preparedStatement = null;
        int rows = 0;
        try {
            // 准备语句对象
            preparedStatement = connection.prepareStatement(sql);
            // 设置语句上的参数， 循环赋值
            for (int i = 0; i < args.length; i++) {
                if (null!=args[i]){
                    preparedStatement.setObject(i + 1, args[i]);
                }else {
                    preparedStatement.setObject(i + 1, 0);
                }
            }
            // 执行 增删改 executeUpdate
            rows = preparedStatement.executeUpdate();
            // 释放资源(可选)
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (null != preparedStatement) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        // 返回的是影响数据库记录数
        return rows;
    }

    //-------------------------------------------------------------------------------------

    //将下划线命名法的字符串转换为驼峰命名法的字符串
    public static String underScoreToCamelCase(String fieldName) {
        String[] words = fieldName.split("_");
        StringBuilder camelCase = new StringBuilder();
        if (words.length > 1) {
            for (int i = 1; i < words.length; i++) {
                camelCase.append(words[i].substring(0, 1).toUpperCase()).append(words[i].substring(1).toLowerCase());
            }
            return words[0] + camelCase.toString();
        }
        return fieldName;
    }

    //---------------------------------------------
    // 公共的查询方法  返回的是单个对象 （求总条数）
    public <T> T baseQueryCount(Class<T> clazz, String sql,Object... args) {
        T t = null;
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        int rows = 0;
        try {
            connection = JDBCUtil.getConn();
            // 准备语句对象
            preparedStatement = connection.prepareStatement(sql);
            // 设置语句上的参数
            for (int i = 0; i < args.length; i++) {
                if (null!=args[i]){
                    preparedStatement.setObject(i + 1, args[i]);
                }else {
                    preparedStatement.setObject(i + 1, 0);
                }
            }
            // 执行 查询
            resultSet = preparedStatement.executeQuery();
            if (resultSet.next()) {
                t = (T) resultSet.getObject(1);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (null != resultSet) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
            if (null != preparedStatement) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return t;
    }

    //------------------------------------------------------------------------------
    //获取添加的主键id的值（用于多对多关系，返回值给中间表添加时用）
    public Integer baseInsertWithId(Connection connection , String sql, String idFieldName, Object... args) {
        // 获取连接
        PreparedStatement preparedStatement = null;
        Integer purchaseId = null;
        int rows = 0;
        try {
            // 准备语句对象
            preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            // 设置语句上的参数
            for (int i = 0; i < args.length; i++) {
                if (null!=args[i]){
                    preparedStatement.setObject(i + 1, args[i]);
                }else {
                    preparedStatement.setObject(i + 1, 0);
                }
            }
            // 执行 增删改 executeUpdate
            rows = preparedStatement.executeUpdate();
            ResultSet rs = preparedStatement.getGeneratedKeys();
            while (rs.next()){
                purchaseId = rs.getInt(1);
            }
            // 释放资源(可选)
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (null != preparedStatement) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        // 返回的是 新增的主键
        return purchaseId;
    }

}
